package top.codecrab.volatile_;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class Data {
    //int x = 0;
    /**
     * Java内存模型在操作主内存（物理内存）的值的时候，会在当前线程所携带的工作内存中拷贝一份，在工作内存中修改完毕后，在传递给主内存
     * 1、volatile保证可见性
     * 被修饰对象在工作内存中被修改后，其他线程能够监测到该对象发生变化并可以获取最新值，称之为可见性
     * 2、volatile不做保证原子性
     * 原子性：一个线程的任务执行时不能被其他线程加塞，要么一起成功，要么一起失败
     * 3、volatile保证时序性
     */
    volatile int x = 0;

    AtomicInteger atomicX = new AtomicInteger();

    public void setX() {
        this.x = 110;
    }

    public void xPlusPlus() {
        this.x++;
    }

    public void atomicPlusPlus() {
        atomicX.getAndIncrement();
    }
}

/**
 * @author codecrab
 * @since 2021年06月28日 15:36
 */
public class VolatileTest {

    /**
     * 验证 volatile 的不保证原子性
     */
    public static void main(String[] args) {
        Data data = new Data();

        for (int i = 1; i <= 20; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    data.xPlusPlus();
                    data.atomicPlusPlus();
                }
            }, String.valueOf(i)).start();
        }

        //如果正在运行的线程超过2个（一个为main线程，另外一个为GC线程），就一直礼让线程
        while (Thread.activeCount() > 2) {
            //当前线程礼让
            Thread.yield();
        }

        System.out.println("int 最后的data.x值：" + data.x);
        System.out.println("atomic 最后的data.x值：" + data.atomicX);
    }

    /**
     * 验证 volatile 的可见性
     */
    public static void testVisibility() {
        Data data = new Data();

        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\tdata的x值：" + data.x);
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            data.setX();
            System.out.println(Thread.currentThread().getName() + "\t修改data的x为：" + data.x);
        }, "AAA").start();

        new Thread(() -> {
            while (data.x == 0) {
                //如果没有监测到data.x的值发生变化，就不会退出循环
            }
            System.out.println(Thread.currentThread().getName() + "\t线程完成，监测到data.x的值发生变化，当前data.x的值：" + data.x);
        }, "BBB").start();

    }

}
